{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Welcome to Lab 3 for Week 1 Day 4\n",
    "\n",
    "Today we're going to build something with immediate value!\n",
    "\n",
    "In the folder `me` I've put a single file `linkedin.pdf` - it's a PDF download of my LinkedIn profile.\n",
    "\n",
    "Please replace it with yours!\n",
    "\n",
    "I've also made a file called `summary.txt`\n",
    "\n",
    "We're not going to use Tools just yet - we're going to add the tool tomorrow."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<table style=\"margin: 0; text-align: left; width:100%\">\n",
    "    <tr>\n",
    "        <td style=\"width: 150px; height: 150px; vertical-align: middle;\">\n",
    "            <img src=\"../assets/tools.png\" width=\"150\" height=\"150\" style=\"display: block;\" />\n",
    "        </td>\n",
    "        <td>\n",
    "            <h2 style=\"color:#00bfff;\">Looking up packages</h2>\n",
    "            <span style=\"color:#00bfff;\">In this lab, we're going to use the wonderful Gradio package for building quick UIs, \n",
    "            and we're also going to use the popular PyPDF PDF reader. You can get guides to these packages by asking \n",
    "            ChatGPT or Claude, and you find all open-source packages on the repository <a href=\"https://pypi.org\">https://pypi.org</a>.\n",
    "            </span>\n",
    "        </td>\n",
    "    </tr>\n",
    "</table>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/Users/sanjayfuloria/Library/Python/3.11/lib/python/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
      "  from .autonotebook import tqdm as notebook_tqdm\n"
     ]
    }
   ],
   "source": [
    "# If you don't know what any of these packages do - you can always ask ChatGPT for a guide!\n",
    "\n",
    "from dotenv import load_dotenv\n",
    "from openai import OpenAI\n",
    "from pypdf import PdfReader\n",
    "import gradio as gr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import ssl\n",
    "import httpx\n",
    "\n",
    "# Create a custom HTTP client that handles SSL certificate issues\n",
    "ssl_context = ssl.create_default_context()\n",
    "ssl_context.check_hostname = False\n",
    "ssl_context.verify_mode = ssl.CERT_NONE\n",
    "\n",
    "http_client = httpx.Client(verify=False)\n",
    "\n",
    "load_dotenv(override=True)\n",
    "openai = OpenAI(timeout=30.0, max_retries=3, http_client=http_client)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "reader = PdfReader(\"me/Sanjay.pdf\")\n",
    "linkedin = \"\"\n",
    "for page in reader.pages:\n",
    "    text = page.extract_text()\n",
    "    if text:\n",
    "        linkedin += text"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "   \n",
      "Contact\n",
      "sanjayfuloria@gmail.com\n",
      "www.linkedin.com/in/sanjayfuloria\n",
      "(LinkedIn)\n",
      "Top Skills\n",
      "Unsupervised Learning\n",
      "Applied Machine Learning\n",
      "Linear Algebra\n",
      "Certifications\n",
      "Mathematics for Machine Learning\n",
      "Programming for Everybody (Getting\n",
      "Started with Python)\n",
      "Capstone: Retrieving, Processing,\n",
      "and Visualizing Data with Python\n",
      "Machine Learning\n",
      "Machine Learning Specialization\n",
      "Sanjay Fuloria Ph.D.\n",
      "Professor and Director Center for Distance and Online Education,\n",
      "ICFAI FOUNDATION FOR HIGHER EDUCATION (a deemed to\n",
      "be University under Section 3 of the UGC Act) , Hyderabad at IBS\n",
      "Hyderabad\n",
      "Hyderabad, Telangana, India\n",
      "Summary\n",
      "I have 26 years of experience in both academics and the corporate\n",
      "world. I have handled marketing and sales, taught market research,\n",
      "analytics and practiced business research, team management and\n",
      "application of various analytics and machine learning tools and\n",
      "techniques.\n",
      "Experience\n",
      "IBS Hyderabad\n",
      "6 years 3 months\n",
      "Professor and Director, Center for Distance and Online Education\n",
      "(CDOE), IFHE University, Hyderabad\n",
      "June 2021 - Present (4 years 3 months)\n",
      "Hyderabad, Telangana, India\n",
      "I am handling online distance education (ODL) and online education programs\n",
      "of ICFAI Foundation for Higher Education (IFHE) University, Hyderabad.\n",
      "This involves program design, curriculum design, online lectures, and other\n",
      "coordination activities.\n",
      "Professor\n",
      "June 2019 - Present (6 years 3 months)\n",
      "Hyderabad Area, India\n",
      "Teaching Advanced Analytics, Business Research Methods, Project\n",
      "Management and other analytical subjects.\n",
      "Cognizant Technology Solutions\n",
      "8 years 5 months\n",
      "General Manager\n",
      "June 2015 - June 2019 (4 years 1 month)\n",
      "Hyderabad Area, India\n",
      "  Page 1 of 2   \n",
      "Handled Research as a Service division of Cognizant as part of the Cognizant\n",
      "Research Center. Was managing research teams. Worked on research\n",
      "and analytics projects for various internationally renowned Fortune 500\n",
      "Companies. Was instrumental in hiring, training, managing, and counselling\n",
      "people.\n",
      "Deputy General Manager\n",
      "February 2011 - June 2015 (4 years 5 months)\n",
      "Hyderabad Area, India\n",
      "Worked on Principal Component Analysis based models. Have hands on\n",
      "experience in using techniques like Conjoint Analysis, RFM models, Customer\n",
      "Life Time Value and Survival Analysis.\n",
      "Education\n",
      "Indian School of Business\n",
      "Executive Education Leadership with AI, Business, Management, Marketing,\n",
      "and Related Support Services · (February 2024 - July 2024)\n",
      "ICFAI Foundation for Higher Education, Hyderabad\n",
      "Doctor of Philosophy - PhD, PhD in Management, Technology and\n",
      "Strategy · (2002 - 2007)\n",
      "Malviya National Institute of Technology, Jaipur\n",
      "Master of Management Studies, MMS, Management- Marketing and\n",
      "IT · (1997 - 1999)\n",
      "Bhilai Institute of Technology (BIT), Durg\n",
      "Bachelor of Engineering - BE (Electronics & Communications), Electronics &\n",
      "Communications · (1992 - 1996)\n",
      "  Page 2 of 2\n"
     ]
    }
   ],
   "source": [
    "print(linkedin)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "with open(\"me/summary.txt\", \"r\", encoding=\"utf-8\") as f:\n",
    "    summary = f.read()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "name = \"Sanjay Fuloria\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "system_prompt = f\"You are acting as {name}. You are answering questions on {name}'s website, \\\n",
    "particularly questions related to {name}'s career, background, skills and experience. \\\n",
    "Your responsibility is to represent {name} for interactions on the website as faithfully as possible. \\\n",
    "You are given a summary of {name}'s background and LinkedIn profile which you can use to answer questions. \\\n",
    "Be professional and engaging, as if talking to a potential client or future employer who came across the website. \\\n",
    "If you don't know the answer, say so.\"\n",
    "\n",
    "system_prompt += f\"\\n\\n## Summary:\\n{summary}\\n\\n## LinkedIn Profile:\\n{linkedin}\\n\\n\"\n",
    "system_prompt += f\"With this context, please chat with the user, always staying in character as {name}.\"\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\"You are acting as Sanjay Fuloria. You are answering questions on Sanjay Fuloria's website, particularly questions related to Sanjay Fuloria's career, background, skills and experience. Your responsibility is to represent Sanjay Fuloria for interactions on the website as faithfully as possible. You are given a summary of Sanjay Fuloria's background and LinkedIn profile which you can use to answer questions. Be professional and engaging, as if talking to a potential client or future employer who came across the website. If you don't know the answer, say so.\\n\\n## Summary:\\nDr.\\u202fSanjay Fuloria is a Professor of Operations Management and Information Technology at ICFAI Business School (IBS), Hyderabad, and currently serves as the Director of the Centre for Distance and Online Education (CDOE) at IFHE University, a Deemed-to-be University in Hyderabad \\n\\nHe earned his B.E. in Electronics & Communications (1996), MMS in Marketing & Systems (1999), and a Ph.D. in Management from ICFAI University, Dehradun in 2007 \\n\\n\\nWith over 25 years of experience, including more than a decade in industry roles at organisations such as HCL and Cognizant Technology Solutions, he transitioned into academia, combining both corporate and scholarly expertise \\n\\n\\nHis academic portfolio encompasses teaching courses in operations management, business analytics, machine learning, statistics, and project management \\n\\nHis research spans areas such as technology policy, innovation, blockchain bibliometrics, passenger demand forecasting using deep learning, disaster management indices, and mobile banking adoption—published across recognised journals including the IUP Journal of Applied Economics and International Journal of Business Forecasting and Marketing Intelligence \\n\\nFrom a pro perspective, Dr.\\u202fFuloria's strengths lie in his applied research combining machine learning and analytics with real-world management and policy relevance. His dual experience in corporate research and academic leadership gives him credibility in integrating emerging technologies like AI and gamification into distance education paradigms. He has also actively shaped practical online learning strategies, as discussed in a 2024 podcast, where he addressed instructional design, hiring challenges of part-time faculty, and the future role of certification versus degree programs \\n\\n\\nOn the con side, one might argue that while his profile highlights applied research and administrative acumen, there is relatively less evidence of significant theoretical contributions in mainstream international journals. Additionally, although his experience in distance and online education is substantial, the field’s rapid evolution—especially post‑2020—demands continuous innovation and robust empirical evaluation. Detailed metrics on program outcomes and student engagement efficacy are areas where publicly accessible data remain somewhat limited.\\n\\nIn conclusion, Professor, Dr.\\u202fSanjay Fuloria presents a rich blend of corporate and academic sensibilities, making him well suited to lead initiatives in analytics-driven education innovation. Yet, from a purely research impact standpoint, his contributions appear more practically oriented than theoretically foundational—a consideration for those evaluating scholarly influence versus applied leadership.\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n## LinkedIn Profile:\\n\\xa0 \\xa0\\nContact\\nsanjayfuloria@gmail.com\\nwww.linkedin.com/in/sanjayfuloria\\n(LinkedIn)\\nTop Skills\\nUnsupervised Learning\\nApplied Machine Learning\\nLinear Algebra\\nCertifications\\nMathematics for Machine Learning\\nProgramming for Everybody (Getting\\nStarted with Python)\\nCapstone: Retrieving, Processing,\\nand Visualizing Data with Python\\nMachine Learning\\nMachine Learning Specialization\\nSanjay Fuloria Ph.D.\\nProfessor and Director Center for Distance and Online Education,\\nICFAI FOUNDATION FOR HIGHER EDUCATION (a deemed to\\nbe University under Section 3 of the UGC Act) , Hyderabad at IBS\\nHyderabad\\nHyderabad, Telangana, India\\nSummary\\nI have 26 years of experience in both academics and the corporate\\nworld. I have handled marketing and sales, taught market research,\\nanalytics and practiced business research, team management and\\napplication of various analytics and machine learning tools and\\ntechniques.\\nExperience\\nIBS Hyderabad\\n6 years 3 months\\nProfessor and Director, Center for Distance and Online Education\\n(CDOE), IFHE University, Hyderabad\\nJune 2021\\xa0-\\xa0Present\\xa0(4 years 3 months)\\nHyderabad, Telangana, India\\nI am handling online distance education (ODL) and online education programs\\nof ICFAI Foundation for Higher Education (IFHE) University, Hyderabad.\\nThis involves program design, curriculum design, online lectures, and other\\ncoordination activities.\\nProfessor\\nJune 2019\\xa0-\\xa0Present\\xa0(6 years 3 months)\\nHyderabad Area, India\\nTeaching Advanced Analytics, Business Research Methods, Project\\nManagement and other analytical subjects.\\nCognizant Technology Solutions\\n8 years 5 months\\nGeneral Manager\\nJune 2015\\xa0-\\xa0June 2019\\xa0(4 years 1 month)\\nHyderabad Area, India\\n\\xa0 Page 1 of 2\\xa0 \\xa0\\nHandled Research as a Service division of Cognizant as part of the Cognizant\\nResearch Center. Was managing research teams. Worked on research\\nand analytics projects for various internationally renowned Fortune 500\\nCompanies. Was instrumental in hiring, training, managing, and counselling\\npeople.\\nDeputy General Manager\\nFebruary 2011\\xa0-\\xa0June 2015\\xa0(4 years 5 months)\\nHyderabad Area, India\\nWorked on Principal Component Analysis based models. Have hands on\\nexperience in using techniques like Conjoint Analysis, RFM models, Customer\\nLife Time Value and Survival Analysis.\\nEducation\\nIndian School of Business\\nExecutive Education Leadership with AI,\\xa0Business, Management, Marketing,\\nand Related Support Services\\xa0·\\xa0(February 2024\\xa0-\\xa0July 2024)\\nICFAI Foundation for Higher Education, Hyderabad\\nDoctor of Philosophy - PhD,\\xa0PhD in Management, Technology and\\nStrategy\\xa0·\\xa0(2002\\xa0-\\xa02007)\\nMalviya National Institute of Technology, Jaipur\\nMaster of Management Studies, MMS,\\xa0Management- Marketing and\\nIT\\xa0·\\xa0(1997\\xa0-\\xa01999)\\nBhilai Institute of Technology (BIT), Durg\\nBachelor of Engineering - BE (Electronics & Communications),\\xa0Electronics &\\nCommunications\\xa0·\\xa0(1992\\xa0-\\xa01996)\\n\\xa0 Page 2 of 2\\n\\nWith this context, please chat with the user, always staying in character as Sanjay Fuloria.\""
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "system_prompt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "def chat(message, history):\n",
    "    messages = [{\"role\": \"system\", \"content\": system_prompt}] + history + [{\"role\": \"user\", \"content\": message}]\n",
    "    response = openai.chat.completions.create(model=\"gpt-4o-mini\", messages=messages)\n",
    "    return response.choices[0].message.content"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Special note for people not using OpenAI\n",
    "\n",
    "Some providers, like Groq, might give an error when you send your second message in the chat.\n",
    "\n",
    "This is because Gradio shoves some extra fields into the history object. OpenAI doesn't mind; but some other models complain.\n",
    "\n",
    "If this happens, the solution is to add this first line to the chat() function above. It cleans up the history variable:\n",
    "\n",
    "```python\n",
    "history = [{\"role\": h[\"role\"], \"content\": h[\"content\"]} for h in history]\n",
    "```\n",
    "\n",
    "You may need to add this in other chat() callback functions in the future, too."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "* Running on local URL:  http://127.0.0.1:7860\n",
      "* To create a public link, set `share=True` in `launch()`.\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div><iframe src=\"http://127.0.0.1:7860/\" width=\"100%\" height=\"500\" allow=\"autoplay; camera; microphone; clipboard-read; clipboard-write;\" frameborder=\"0\" allowfullscreen></iframe></div>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": []
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "gr.ChatInterface(chat, type=\"messages\").launch()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## A lot is about to happen...\n",
    "\n",
    "1. Be able to ask an LLM to evaluate an answer\n",
    "2. Be able to rerun if the answer fails evaluation\n",
    "3. Put this together into 1 workflow\n",
    "\n",
    "All without any Agentic framework!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create a Pydantic model for the Evaluation\n",
    "\n",
    "from pydantic import BaseModel\n",
    "\n",
    "class Evaluation(BaseModel):\n",
    "    is_acceptable: bool\n",
    "    feedback: str\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "evaluator_system_prompt = f\"You are an evaluator that decides whether a response to a question is acceptable. \\\n",
    "You are provided with a conversation between a User and an Agent. Your task is to decide whether the Agent's latest response is acceptable quality. \\\n",
    "The Agent is playing the role of {name} and is representing {name} on their website. \\\n",
    "The Agent has been instructed to be professional and engaging, as if talking to a potential client or future employer who came across the website. \\\n",
    "The Agent has been provided with context on {name} in the form of their summary and LinkedIn details. Here's the information:\"\n",
    "\n",
    "evaluator_system_prompt += f\"\\n\\n## Summary:\\n{summary}\\n\\n## LinkedIn Profile:\\n{linkedin}\\n\\n\"\n",
    "evaluator_system_prompt += f\"With this context, please evaluate the latest response, replying with whether the response is acceptable and your feedback.\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [],
   "source": [
    "def evaluator_user_prompt(reply, message, history):\n",
    "    user_prompt = f\"Here's the conversation between the User and the Agent: \\n\\n{history}\\n\\n\"\n",
    "    user_prompt += f\"Here's the latest message from the User: \\n\\n{message}\\n\\n\"\n",
    "    user_prompt += f\"Here's the latest response from the Agent: \\n\\n{reply}\\n\\n\"\n",
    "    user_prompt += \"Please evaluate the response, replying with whether it is acceptable and your feedback.\"\n",
    "    return user_prompt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "gemini = OpenAI(\n",
    "    api_key=os.getenv(\"GOOGLE_API_KEY\"), \n",
    "    base_url=\"https://generativelanguage.googleapis.com/v1beta/openai/\"\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [],
   "source": [
    "def evaluate(reply, message, history) -> Evaluation:\n",
    "\n",
    "    messages = [{\"role\": \"system\", \"content\": evaluator_system_prompt}] + [{\"role\": \"user\", \"content\": evaluator_user_prompt(reply, message, history)}]\n",
    "    response = gemini.beta.chat.completions.parse(model=\"gemini-2.0-flash\", messages=messages, response_format=Evaluation)\n",
    "    return response.choices[0].message.parsed"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [],
   "source": [
    "messages = [{\"role\": \"system\", \"content\": system_prompt}] + [{\"role\": \"user\", \"content\": \"do you hold a patent?\"}]\n",
    "response = openai.chat.completions.create(model=\"gpt-4o-mini\", messages=messages)\n",
    "reply = response.choices[0].message.content"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "reply"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "evaluate(reply, \"do you hold a patent?\", messages[:1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [],
   "source": [
    "def rerun(reply, message, history, feedback):\n",
    "    updated_system_prompt = system_prompt + \"\\n\\n## Previous answer rejected\\nYou just tried to reply, but the quality control rejected your reply\\n\"\n",
    "    updated_system_prompt += f\"## Your attempted answer:\\n{reply}\\n\\n\"\n",
    "    updated_system_prompt += f\"## Reason for rejection:\\n{feedback}\\n\\n\"\n",
    "    messages = [{\"role\": \"system\", \"content\": updated_system_prompt}] + history + [{\"role\": \"user\", \"content\": message}]\n",
    "    response = openai.chat.completions.create(model=\"gpt-4o-mini\", messages=messages)\n",
    "    return response.choices[0].message.content"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [],
   "source": [
    "def chat(message, history):\n",
    "    if \"patent\" in message:\n",
    "        system = system_prompt + \"\\n\\nEverything in your reply needs to be in pig latin - \\\n",
    "              it is mandatory that you respond only and entirely in pig latin\"\n",
    "    else:\n",
    "        system = system_prompt\n",
    "    messages = [{\"role\": \"system\", \"content\": system}] + history + [{\"role\": \"user\", \"content\": message}]\n",
    "    response = openai.chat.completions.create(model=\"gpt-4o-mini\", messages=messages)\n",
    "    reply =response.choices[0].message.content\n",
    "\n",
    "    evaluation = evaluate(reply, message, history)\n",
    "    \n",
    "    if evaluation.is_acceptable:\n",
    "        print(\"Passed evaluation - returning reply\")\n",
    "    else:\n",
    "        print(\"Failed evaluation - retrying\")\n",
    "        print(evaluation.feedback)\n",
    "        reply = rerun(reply, message, history, evaluation.feedback)       \n",
    "    return reply"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "gr.ChatInterface(chat, type=\"messages\").launch()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "✅ API connection successful!\n",
      "Test response: Hello! It looks like you're testing the system. How can I assist you today?\n"
     ]
    }
   ],
   "source": [
    "# Test OpenAI API connection with SSL bypass\n",
    "try:\n",
    "    test_response = openai.chat.completions.create(\n",
    "        model=\"gpt-4o-mini\",\n",
    "        messages=[{\"role\": \"user\", \"content\": \"Hello, this is a test\"}],\n",
    "        max_tokens=20\n",
    "    )\n",
    "    print(\"✅ API connection successful!\")\n",
    "    print(\"Test response:\", test_response.choices[0].message.content)\n",
    "except Exception as e:\n",
    "    print(\"❌ API connection still failing:\", str(e))\n",
    "    print(\"Error type:\", type(e).__name__)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
